☰ See All Chapters |
Java Streams
The Stream is an interface which is located in new package called java.util.stream in Java 8. The Stream interface is designed for lambdas, functional-style programming in Java 8.
When we watch videos from any site (YouTube) or any app, a section of video is first buffered into your system and start playing, Once this section is played, a next section will be buffered and start playing and so on. This is called as streaming. When we watch video, entire video will not be downloaded into system. Similarly in java, Stream interface executes lazily.
A stream is not a data structure; it represents a sequence of objects from a source such as a data structure/collection, an array, or an I/O channel as per the pipelined methods. Stream is somewhat like the Iterator interface, do not support indexed access. However, Stream supports parallel execution. To improve performance and to support primitive int, double and long-valued elements, we have corresponding primitive streams IntStream, DoubleStream, and LongStream.
Generating Streams
Collections/Array Streaming
In java 8 many existing Java library classes have introduces Stream returning methods.
We most often create Streams form Collection. The Collection interface has introduced two default methods on it for creating streams:
stream(): Returns a sequential Stream
parallelStream(): Returns a possibly parallel Stream
package com.java4coding;
import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream;
public class Demo {
public static void main(String[] args) { Stream<Integer> stream1 = Stream.of(1,2,3,4,5); stream1.forEach(p -> System.out.println(p));
System.out.println("****************************************************"); Stream<Integer> stream2 = Stream.of( new Integer[]{1,2,3,4,5} ); stream2.forEach(p -> System.out.println(p));
System.out.println("****************************************************"); List<String> list = new ArrayList<String>(); list.add("Manu"); list.add("Advith"); list.add("Tyagraj"); Stream<String> stream3 = list.stream(); stream3.forEach(p -> System.out.println(p));
System.out.println("****************************************************"); IntStream stream4 = "hello_world".chars(); stream4.forEach(p -> System.out.println(p)); } } |
Infinite Streaming
Using the generate or iterate static methods of Stream, you can create an infinite Stream of values. For example, below Stream will print “Hello” infinitely, this is equivalent of while(true) {System.out.println("Hello");}
package com.java4coding;
import java.util.stream.Stream;
public class Demo {
public static void main(String[] args) { Stream<String> stream = Stream.generate(() -> { return "Hello"; }); stream.forEach(p -> System.out.println(p)); } } |
To create infinite supply of objects, you could call generate in the following way:
Stream.generate(() -> new Employee());
So generate of Stream can be used stream the values continuously and forever.
Developer should use it carefully as it could lead to infinite memory generation and could cause the JVM to crash.
The difference between generate and iterate is that, generate creates the infinite Stream of constant value. iterate creates the infinite Stream of changing value. iterate takes has two parameters, an initial value and a Function that modifies that value. For example, below code doubles the value in each iteration:
package com.java4coding;
import java.util.stream.Stream;
public class Demo { public static void main(String[] args) { Stream.iterate(1, i -> i*2).forEach(System.out::println); } } |
You should execute the above code with caution, the value that doubles, reaches the java range limit within a second and finally it prints 0 on console. We will not be able to see the doubled values on the console. Or some time JVM crashes.
Generating Streams within the range
IntStream, DoubleStream, and LongStream has method, range(intialValue, FinalValue) for creating ranges of numbers.
package com.java4coding;
import java.util.stream.IntStream;
public class Demo { public static void main(String[] args) { IntStream.range(1, 10).forEach(System.out::println); } } |
Different Operations on Streams
package com.java4coding;
import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors;
public class Demo { public static void main(String[] args) {
//1. Using map, we can transform the items in the collection to the objects by applying the function passed as argument. //Using collect, we can convert result of the intermediate operations performed on the streams to collections List<Integer> numList = Arrays.asList(1, 2, 3, 4); List<Integer> squareList = numList.stream().map(i -> i * i).collect(Collectors.toList()); System.out.println(squareList);// Prints [1, 4, 9, 16]
System.out.println("******************************************************************"); //2. Using filter, we can filter the elements of this stream that match the given predicate. List<String> names = Arrays.asList("Lav", "Manu", "Advith", "Tyagraj", "Likitha", "Raj"); List<String> filteredNames = names.stream().filter(s -> s.length() > 3).collect(Collectors.toList()); System.out.println(filteredNames);//Prints [Manu, Advith, Tyagraj, Likitha]
System.out.println("******************************************************************"); //3. Using sorted, we can sort the stream List <String> sortedNames = names.stream().sorted().collect(Collectors.toList()); System.out.println(sortedNames);//Prints [Advith, Lav, Likitha, Manu, Raj, Tyagraj]
System.out.println("******************************************************************"); //4. Using collect we can convert result of the intermediate operations performed on the streams to collections List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); //Converting to list List<Integer> evenNumbersList = numbers.stream().filter(i -> i % 2 == 0).collect(Collectors.toList()); System.out.println(evenNumbersList);//Prints [2, 4, 6, 8]
//Converting to set Set<Integer> oddNumbersSet = numbers.stream().filter(i -> i % 2 == 1).collect(Collectors.toSet()); System.out.println(oddNumbersSet);//Prints [1, 3, 5, 7, 9]
System.out.println("******************************************************************"); //5. Using toArray we can convert result of the intermediate operations performed on the streams to array Integer[] evenNumbersArr = numbers.stream().filter(i -> i % 2 == 0).toArray(Integer[]::new); for(int i : evenNumbersArr) {System.out.print(i + " ");}//Prints 2 4 6 8
System.out.println("\n******************************************************************"); //6. Using forEach, we can iterate through every element of the stream List<String> memberNames = Arrays.asList("Lav", "Manu", "Advith", "Tyagraj", "Likitha", "Raj"); memberNames.stream().sorted().forEach(name -> System.out.print(name + " "));//Prints Advith Lav Likitha Manu Raj Tyagraj
System.out.println("\n******************************************************************"); //7. Using reduce, we can perform a reduction on the elements of the stream with the given BiFunction. //The result is an Optional holding the reduced value. Optional<String> commaSeparatedNames = memberNames.stream().reduce((s1, s2) -> s1 + "," + s2); System.out.println(commaSeparatedNames);//Prints Optional[Lav,Manu,Advith,Tyagraj,Likitha,Raj] } } |
Parallel Streams
Java is sequential programming language, executing the statements sequentially is much better. Whenever required we can implement multithreading. But suppose if we have a list of 10 thousand objects and we have you extract the details form one particular object which is residing at last position of the list, then it would consume lot of time and performance matters if we iterate through all 10 thousand records sequentially. We can split the list, we can write the separate threads to get the required object, but this would be again not a neat solution just to iterate a list.
Java 8 has a simple solution for this, i.e. parallel stream.
Stream implementation in Java is by default sequential unless until it is explicitly mentioned in parallel.
JVM will not perform complete parallel operation on each elements. Suppose if there are ten thousand elements in stream then it would require 10 thousand threads to do complete parallel operation. What JVM does is, it partitions the stream into multiple sub-streams, process these sub-streams in parallel and then combine the results.
Parallel Streams can be used, if Sequential Streams has performance issue. If the size of the collection is considerably less, then using parallel stream will take more time than sequential stream as it involves multiple threads. Parallel stream should be used if the size of the collection is very large or the objects in the stream are heavy weight. If the size of the element is large, then we can use parallel stream with less collection size. Therefore, the collection size and element size both matters.
To create parallel stream, we have to call the parallelStream() method.
package com.java4coding;
import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream;
public class Demo { public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>(); for(int i = 1; i< 100000; i++){ list.add(i); }
List<Integer> evenNumbersList = null;
long time_1 = System.nanoTime();
Stream<Integer> parallelStream = list.parallelStream(); evenNumbersList = parallelStream.filter(i -> i%2 == 0).collect(Collectors.toList()); System.out.println(evenNumbersList);
long time_2 = System.nanoTime(); System.out.println( "With Parallel Stream " + (time_2 - time_1) + " milliseconds" );
long time_3 = System.nanoTime();
Stream<Integer> sequentialStream = list.stream(); evenNumbersList = sequentialStream.filter(i -> i%2 == 0).collect(Collectors.toList()); System.out.println(evenNumbersList);
long time_4 = System.nanoTime(); System.out.println("With Sequential Stream " + (time_4 - time_3) + " milliseconds" ); } } |
Output:
All Chapters